package cn.iocoder.yudao.module.aiposter.remote;

import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.module.aiposter.config.PosterConfig;
import cn.iocoder.yudao.module.aiposter.config.RemoteConfig;
import cn.iocoder.yudao.module.aiposter.remote.minio.ViewContentType;
import cn.iocoder.yudao.module.aiposter.remote.vo.PreSignedUrlResp;
import cn.iocoder.yudao.module.aiposter.utils.CommonUtils;
import io.minio.*;
import io.minio.errors.MinioException;
import io.minio.http.Method;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;
import sun.misc.BASE64Encoder;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 图床工具类
 * <p>
 * 上传图片
 */
@Component
@Slf4j
public class ImgClient {

    private static final String BUCKET = "cw-poster";
    private static final String BASE_PATH = "/images";
    private static final String NORMAL_PATH = "/normal";
    private static final String SEGMENTATION_PATH = "/segmentation";
    private static final String COVER_PATH = "/cover";
    private static final String POSTER_PATH = "/poster";

    private final MinioClient minioClient;

    private final RemoteConfig remoteConfig;
    private final PosterConfig posterConfig;

    public ImgClient(RemoteConfig remoteConfig, PosterConfig posterConfig) {
        this.remoteConfig = remoteConfig;
        this.posterConfig = posterConfig;
        // Create a minioClient with the MinIO server playground, its access key and secret key.
        this.minioClient = MinioClient.builder()
                .endpoint(remoteConfig.getMinioHost())
                .credentials(remoteConfig.getMinioKey(), remoteConfig.getMinioSecret())
                .build();
        try {
            boolean found =
                    minioClient.bucketExists(BucketExistsArgs.builder().bucket(BUCKET).build());
            if (!found) {
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(BUCKET).build());
            } else {
                log.info("Bucket 'cw-poster' already exists.");
            }
        } catch (MinioException | IOException | InvalidKeyException | NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
    }

    public PreSignedUrlResp genPreSignedURL(String objectName) {
        if (objectName.startsWith("/")) {
            objectName = objectName.substring(1);
        }
        String remotePath = BASE_PATH + NORMAL_PATH + File.separator + objectName;
        try {
            String preSignedUrl = minioClient.getPresignedObjectUrl(GetPresignedObjectUrlArgs.builder()
                    .bucket(BUCKET)
                    .method(Method.PUT)
                    .expiry(1, TimeUnit.DAYS)
                    .object(remotePath)
                    .build());
            return PreSignedUrlResp.builder()
                    .preSignedUrl(preSignedUrl)
                    .imgUrl(remoteConfig.getMinioHost() + File.separator + BUCKET + remotePath)
                    .build();
        } catch (MinioException e) {
            System.out.println("Error occurred: " + e);
            System.out.println("HTTP trace: " + e.httpTrace());
        } catch (IOException | InvalidKeyException | NoSuchAlgorithmException e) {
            System.out.println("Error occurred: " + e);
        }
        return null;
    }

    public String getImgBase64(String remotePath) {
        if (remotePath == null) {
            return null;
        }
        InputStream inputStream = null;
        ByteArrayOutputStream outputStream = null;
        try {
            String mediaType = remotePath.substring(remotePath.lastIndexOf(".") + 1);
            if (remotePath.startsWith("http")) {
                inputStream = getImgStreamWithUrl(remotePath);
            } else {
                inputStream = getImgStreamWithClient(remotePath);
            }
            if (inputStream != null) {
                outputStream = new ByteArrayOutputStream();
                int len;
                byte[] buf = new byte[1024];
                while ((len = inputStream.read(buf)) != -1) {
                    outputStream.write(buf, 0, len);
                }
                outputStream.flush();
                byte[] data = outputStream.toByteArray();
                BASE64Encoder encoder = new BASE64Encoder();
                return encoder.encode(data);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (inputStream != null) {
                    inputStream.close();
                }
                if (outputStream != null) {
                    outputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    /**
     * 上传图片至Minio，用于一般文件上传
     *
     * @param fileName 待上传文件地址 全路径
     * @return 返回minio文件地址
     */
    public String uploadNormal(String fileName) {
        String uploadName = CommonUtils.generateNewFileName(fileName);
        return upload(fileName, NORMAL_PATH, uploadName);
    }

    /**
     * 上传图片至Minio，用于一般文件上传
     *
     * @param fileName 待上传文件地址 全路径
     * @return 返回minio文件地址
     */
    public String uploadPoster(String fileName) {
        String uploadName = CommonUtils.generateNewFileName(fileName);
        return upload(fileName, POSTER_PATH, uploadName);
    }

    /**
     * 上传图片至Minio，用于一般文件上传
     *
     * @param fileName 待上传文件地址 全路径
     * @return 返回minio文件地址
     */
    public String uploadNormalWithStream(String fileName, InputStream inputStream, long objSize) {
        String uploadName = CommonUtils.generateNewFileName(fileName);
        return uploadStream(inputStream, objSize, NORMAL_PATH, uploadName);
    }

    /**
     * 上传图片至Minio，用于网页上传文件
     *
     * @param multipartFile http上传文件信息
     * @return 返回minio文件地址
     */
    public String uploadNormalWithHttpUpload(MultipartFile multipartFile) throws IOException {
        // 获取临时本地保存文件名
        SimpleDateFormat df = new SimpleDateFormat(DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND_2);
        String fileName = df.format(new Date()) + "_" + multipartFile.getOriginalFilename();
        String uploadName = CommonUtils.generateNewFileName(fileName);
        return uploadStream(multipartFile.getInputStream(), multipartFile.getSize(), NORMAL_PATH, uploadName);
    }

    /**
     * 上传图片至Minio，用于封面图上传
     *
     * @param fileNameWithPath 待上传文件地址 全路径
     * @return 返回minio文件地址
     */
    public String uploadCover(String fileNameWithPath) {
        String uploadName = CommonUtils.generateNewFileName(fileNameWithPath);
        return upload(fileNameWithPath, COVER_PATH, uploadName);
    }

    /**
     * 上传图片至Minio，用于封面图上传
     *
     * @param base64 待上传的base64内容
     * @param suffix 后缀  png jpg jpeg
     * @return 返回minio文件地址
     */
    public String uploadNormalBase64(String base64, String suffix) {
        String tmpFilePath = CommonUtils.saveBase64(base64, suffix, posterConfig.getLocalUploadDir());
        String uploadName = CommonUtils.generateNewFileName(tmpFilePath);
        return upload(tmpFilePath, NORMAL_PATH, uploadName);
    }

    /**
     * 上传图片至Minio，用于封面图上传
     *
     * @param base64 待上传的base64内容
     * @param suffix 后缀  png jpg jpeg
     * @return 返回minio文件地址
     */
    public String uploadSegmentationBase64(String base64, String suffix) {
        String tmpFilePath = CommonUtils.saveBase64(base64, suffix, posterConfig.getLocalUploadDir());
        String uploadName = CommonUtils.generateNewFileName(tmpFilePath);
        return upload(tmpFilePath, SEGMENTATION_PATH, uploadName);
    }

    /**
     * 生成完成的资源文件路径
     *
     * @param imgPath /xxx/aaa.png
     * @return http://{OSS_HOST}/xxx/aaa.png
     */
    public String fixImgUrl(String imgPath) {
        if (StringUtils.isBlank(imgPath)) {
            return null;
        }
        if (imgPath.startsWith("http")) {
            return imgPath;
        }
        return remoteConfig.getMinioHost() + "/" + BUCKET + imgPath;
    }

    /**
     * 上传图片至Minio
     *
     * @param fileNameWithPath 待上传文件地址 全路径
     * @param uploadName       上传文件远程文件名
     * @return 返回minio文件地址
     */
    private String upload(String fileNameWithPath, String uploadPath, String uploadName) {
        String remotePath = BASE_PATH + uploadPath + File.separator + uploadName;
        try {
            minioClient.uploadObject(
                    UploadObjectArgs.builder()
                            .bucket(BUCKET)
                            .object(remotePath)
                            .contentType(ViewContentType.getContentType(remotePath))
                            .filename(fileNameWithPath)
                            .build());
            return fixImgUrl(remotePath);
        } catch (MinioException e) {
            System.out.println("Error occurred: " + e);
            System.out.println("HTTP trace: " + e.httpTrace());
        } catch (IOException | InvalidKeyException | NoSuchAlgorithmException e) {
            System.out.println("Error occurred: " + e);
        }
        return null;
    }

    /**
     * 文件流上传图片至Minio
     *
     * @param inputStream 文件流
     * @param objectSize  文件大小
     * @param uploadName  上传文件远程文件名
     * @return 返回minio文件地址
     */
    private String uploadStream(InputStream inputStream, long objectSize, String uploadPath, String uploadName) {
        String remotePath = BASE_PATH + uploadPath + File.separator + uploadName;
        try {
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(BUCKET)
                            .object(remotePath)
                            .contentType(ViewContentType.getContentType(remotePath))
                            .stream(inputStream, objectSize, 1024 * 1024 * 5 + 1)
                            .build());
            return fixImgUrl(remotePath);
        } catch (MinioException e) {
            System.out.println("Error occurred: " + e);
            System.out.println("HTTP trace: " + e.httpTrace());
        } catch (IOException | InvalidKeyException | NoSuchAlgorithmException e) {
            System.out.println("Error occurred: " + e);
        }
        return null;
    }

    /**
     * 读取远程文件
     *
     * @param imgUrl http连接
     * @return 返回minio文件地址
     */
    private InputStream getImgStreamWithUrl(String imgUrl) {
        if (null == imgUrl || !imgUrl.startsWith("http")) {
            return null;
        }
        HttpURLConnection httpUrl;
        try{
            URL url = new URL(imgUrl);
            httpUrl = (HttpURLConnection) url.openConnection();
            httpUrl.connect();
            httpUrl.getInputStream();
            return httpUrl.getInputStream();
        }catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 读取远程文件
     *
     * @param remotePath 不包含bucket的url路径
     * @return 返回minio文件地址
     */
    private InputStream getImgStreamWithClient(String remotePath) {
        try {
            return minioClient.getObject(
                    GetObjectArgs.builder()
                            .bucket(BUCKET)
                            .object(remotePath)
                            .build());
        } catch (MinioException e) {
            System.out.println("Error occurred: " + e);
            System.out.println("HTTP trace: " + e.httpTrace());
        } catch (IOException | InvalidKeyException | NoSuchAlgorithmException e) {
            System.out.println("Error occurred: " + e);
        }
        return null;
    }
}
